中国矿业大学信控学院
补一下我之前在博客园发布的内容
懒得调了,想复制完整代码直接复制最下面的,想复制分布代码去看我博客园链接吧
《数据结构与算法分析》课程设计——贪吃蛇问题 - 刷子zz - 博客园
一、问题描述#
以数据结构思想设计实现贪吃蛇小游戏。
二、需求分析#
首先需要考虑如何设计一个win运行窗口来实时显示结果
然后考虑到蛇的身子是一节一节的,此时最容易联想到的数据结构就是顺序表,链表,如果把蛇比做顺序表或者链表,在之后吃到食物的时候,身子肯定会变长,这就涉及到插入的操作,所以为了更高的效率,我们用链表实现我们的蛇的部分,最初我们把蛇身子按照四个结点打印在屏幕。
对于蛇的移动,在屏幕上面蛇的移动看起来是整个身子向前方平移一个单位,但是其原理是我们在屏幕的另一个地方把蛇从新打印一遍,又把之前的蛇身子去除掉。
对于食物的产生,随机的在地图中产生一个节点,在蛇的头坐标和食物的坐标重复的时候,食物消失,蛇的身子加长,也就是蛇的节点数增加一个。
蛇在其中的几种状态,正常状态:蛇头节点的坐标没有和墙的坐标以及自己身子的坐标重合,
被自己杀死:蛇头的坐标和蛇身子的坐标重合,
撞墙:蛇头的坐标和墙的坐标重合。
三、算法设计#
1.相关变量。#
1 1.相关变量。
2 int JudgeSum = 0; //判断是否加快
3 int Pause = 200000000; //暂停速度(移动速度)
4 int * PJ = &JudgeDirection; //用指针传值判断移动方向
5 nakebody *end = NULL; //尾节点
2.创建链表 #
贪吃蛇的身体如何保存是游戏的核心,所以我们需要用到链表来保存蛇的身体,这样就可以随时知道蛇身数据。
1 typedef struct Snakebody
2 {
3 int x, y; //蛇身的坐标
4 struct Snakebody *next;//保存下一个蛇身的地址
5 }Snakebody; //通过typedef将 Snakebody 替代 struct Snakebody
3.记录食物出现的坐标。#
1 typedef struct Snakexy
2 {
3 int x;
4 int y;
5 }Snakexy; //记录食物坐标
4.绘制初始界面和游戏地图。#
![复制代码](https://img-blog.csdnimg.cn/img_convert/f81ad2b11922319a050f33a7c4e6cf7c.gif)
1 #include
2 #define HEIGHT 20 //设置地图高度
3 #define WIDTH 40 //设置地图宽度
4 #define PRINTF printf("■");
5 #define LINE printf("\n");
6 #define EMPTY printf(" "); //因为这三个语句经常用,所以我就定义成了宏
7 void Front(); //绘制初始界面
8 void DeawMap(); //绘制地图
9
10 void Front()
11 {
12 SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_INTENSITY | FOREGROUND_RED | FOREGROUND_BLUE);//设置红色和蓝色相加
13 MoveCursor(18, 15);
14 printf("请等待......");
15 for (int i = 0; i x = 5 - i;
11 Pbady->y = 5;
12 if (Phead == NULL)
13 {
14 Phead = Pbady;
15 }
16 else
17 {
18 end->next = Pbady;
19 }
20 Pbady->next = NULL;
21 end = Pbady;
22 }
23 Phead_1 = Phead;
24 while (Phead_1->next != NULL)//打印蛇身
25 {
26 MoveCursor(Phead_1->x, Phead_1->y);
27 PRINTF
28 Phead_1 = Phead_1->next;
29 }
30 }
![复制代码](https://img-blog.csdnimg.cn/img_convert/9bfb9a8bcd73e5cd41564e6e87bd8d5f.gif)
6.产生食物,随机产生食物,如果和蛇身体重合则再次随机产生食物。#
![复制代码](https://img-blog.csdnimg.cn/img_convert/a3b82aaae933dc56ca9f2e6e119e2136.gif)
1 #include
2 int sum = 0; //计算得分
3 Snakexy * Food = NULL; //保存食物位置
4 void FoodRand(); //生成食物
5 void FoodRand()
6 {
7 srand((int)time(0));
8 int x = rand() % 27 + 2;//生成随机数
9 int y = rand() % 17 + 2;
10 Phead_1 = Phead;
11 for (int i = 0; i x == x && Phead_1->y == y)
14 {
15 x = rand() % 27 + 2;
16 y = rand() % 17 + 2;
17 }
18 else
19 {
20 Phead_1 = Phead_1->next;
21 }
22 if (Phead_1->next == NULL)
23 {
24 break;
25 }
26 }
27 MoveCursor(x, y);
28 PRINTF
29 Food = (Snakexy*)malloc(sizeof(Snakexy));
30 Food->x = x;
31 Food->y = y;
32 MoveCursor(33, 5);
33 printf(" ");
34 Showf();
35 sum++;
36 SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_INTENSITY);// 蓝
37 }
![复制代码](https://img-blog.csdnimg.cn/img_convert/9bfb9a8bcd73e5cd41564e6e87bd8d5f.gif)
rand函数功能为获取一个伪随机数,如要产生[m,n]范围内的随机数num,可用int num=rand()%(n-m+1)+m;
7.游戏刷新和暂停 ,按回车可暂停游戏。#
![复制代码](https://img-blog.csdnimg.cn/img_convert/0824dedf5587b36b9a0983eda0f3fdb2.gif)
1 int JudgeDirection = 4; //判断方向
2 void ControlMove(); //控制移动和暂停
3 void ControlMove()
4 {
5 if (GetAsyncKeyState(VK_UP) && 0x8000)
6 {
7 if (JudgeDirection == 2)
8 {
9 }
10 else
11 {
12 JudgeDirection = 1;
13 }
14 }
15 if (GetAsyncKeyState(VK_DOWN) && 0x8000)
16 {
17 if (JudgeDirection == 1)
18 {
19 }
20 else
21 {
22 JudgeDirection = 2;
23 }
24 }
25 if (GetAsyncKeyState(VK_RIGHT) && 0x8000)
26 {
27 if (JudgeDirection == 3)
28 {
29 }
30 else
31 {
32 JudgeDirection = 4;
33 }
34 }
35 if (GetAsyncKeyState(VK_LEFT) && 0x8000)
36 {
37 if (JudgeDirection == 4)
38 {
39 }
40 else
41 {
42 JudgeDirection = 3;
43 }
44 }
45 if (GetAsyncKeyState(VK_RETURN) && 0x0D)//判断回车
46 {
47 while (1)
48 {
49 if (GetAsyncKeyState(VK_RETURN) && 0x0D)//再次回车退出死循环
50 {
51 break;
52 }
53 }
54 }
55 }
![复制代码](https://img-blog.csdnimg.cn/img_convert/6d04315a77f1ee6519aa8f682235584b.gif)
GetAsyncKeyState()确定用户当前是否按下了键盘上的一个键
8.显示分数和难度,更新分数和难度。#
![复制代码](https://img-blog.csdnimg.cn/img_convert/07d00a33710e8408264387ff16e8d936.gif)
1 int sum = 0; //计算得分
2 int Hard = 0; //计算难度
3 void Showf(); //显分数以及难度
4 void Showf()
5 {
6 SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_BLUE);// 蓝
7 MoveCursor(33, 5);
8 printf("得分:%d", sum);
9 MoveCursor(33, 6);
10 printf("难度:%d", Hard);
11 }
![复制代码](https://img-blog.csdnimg.cn/img_convert/45ae163f7cecd9f7e0e6cdc9cd1cabcd.gif)
9.移动光标 ,游戏不闪的原因就是我们只绘制一次地图 然后用光标定点刷新目标点。#
![复制代码](https://img-blog.csdnimg.cn/img_convert/6180b1fbeb94561128971aaae0303b2a.gif)
1 void MoveCursor(int x, int y); //移动光标
2 void MoveCursor(int x, int y)//设置光标位置(就是输出显示的开始位置)
3 {
4 COORD pos = { x * 2,y };
5 HANDLE output = GetStdHandle(STD_OUTPUT_HANDLE);//获得标准输出的句柄
6 SetConsoleCursorPosition(output, pos); //设置光标位置
7 }
![复制代码](https://img-blog.csdnimg.cn/img_convert/1df06fd9165f2b5dc7aa410b16687c38.gif)
COORD是Windows API中定义的一种结构体
10.检测,检测是否吃到食物,是否撞墙,是否撞到自己。#
![复制代码](https://img-blog.csdnimg.cn/img_convert/6916655fc102222f85d2e2029a78553a.gif)
1 void Jfood(); //检测是否吃到食物
2 void Jwall(); //检测蛇头是否撞墙
3 void Jsnake(); //检测蛇头是否撞到蛇身
4 void Jfood()
5 {
6 Phead_1 = Phead;
7 if (Phead_1->x == Food->x&&Phead_1->y == Food->y)
8 {
9 FoodRand();
10 JudgeSum += 1;
11 if (JudgeSum == 5)
12 {
13 JudgeSum = 0;//如果JudgeSum等于5则从新判断
14 Hard += 1;
15 Pause -= 20000000;//每成立一次循环减少20000000
16 }
17 while (Phead_1->next != NULL)
18 {
19 Phead_1 = Phead_1->next;
20 }
21 Snakebody *S = (Snakebody*)malloc(sizeof(Snakebody));
22 S->x = Food->x;
23 S->y = Food->y;
24 S->next = NULL;
25 Phead_1->next = S;
26 ControlMove();
27 MoveCursor(Phead_1->x, Phead_1->y);
28 PRINTF
29 }
30 //获取食物的坐标和蛇头做对比
31 }
32 void Jwall()
33 {
34 if (Phead->x == 0 || Phead->x == 29 || Phead->y == 0 || Phead->y == 19)
35 {
36 MoveCursor(10, 20);
37 SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_INTENSITY | FOREGROUND_RED);//设置红色
38 printf("抱歉,你撞到了自己,游戏结束! ");
39 system("pause>nul");
40 exit(0);
41 }
42 }
43 void Jsnake()
44 {
45 Phead_1 = Phead->next;
46 while (Phead_1->next != NULL)
47 {
48 if ((Phead->x == Phead_1->x) && (Phead->y == Phead_1->y))
49 {
50 MoveCursor(10, 20);
51 SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_INTENSITY | FOREGROUND_RED);//设置红色
52 printf("抱歉,你撞到了自己,游戏结束! ");
53 system("pause>nul");
54 exit(0);
55 }
56 Phead_1 = Phead_1->next;
57 }
58 }
![复制代码](https://img-blog.csdnimg.cn/img_convert/be8867eb698e4066bc6441a277d5fbc8.gif)
11.游戏循环#
![复制代码](https://img-blog.csdnimg.cn/img_convert/01b4e71d54c714c82386e46d62e3b261.gif)
1 void Move(); //游戏运行
2 void Move()
3 {
4 while (1)
5 {
6 Phead_1 = Phead;
7 while (Phead_1->next->next != NULL)
8 {
9 Phead_1 = Phead_1->next;
10 }
11 Phead_1->next = NULL;
12 for (int i = 0; i < Pause; i++) {}
13 ControlMove();
14 MoveCursor(Phead_1->x, Phead_1->y);
15 EMPTY
16 //上面为消除尾部
17 Snakebody *Phead_2 = (Snakebody*)malloc(sizeof(Snakebody));
18 if (*PJ == 1)
19 {
20 Phead_2->x = Phead->x;
21 Phead_2->y = Phead->y - 1;
22 }
23 if (*PJ == 2)
24 {
25 Phead_2->x = Phead->x;
26 Phead_2->y = Phead->y + 1;
27 }
28 if (*PJ == 3)
29 {
30 Phead_2->x = Phead->x - 1;
31 Phead_2->y = Phead->y;
32 }
33 if (*PJ == 4)
34 {
35 Phead_2->x = Phead->x + 1;
36 Phead_2->y = Phead->y;
37 }
38 Phead_2->next = Phead;
39 Phead = Phead_2;
40 MoveCursor(Phead_2->x, Phead_2->y);
41 PRINTF
42 Jfood();
43 Jwall();
44 Jsnake();
45 MoveCursor(40, 20);
46 }
47 }
![复制代码](https://img-blog.csdnimg.cn/img_convert/945425f0e14ff442a03271e724d684fc.gif)
12.释放内存#
![复制代码](https://img-blog.csdnimg.cn/img_convert/ad5a7aeba8045cedb0316fa202623584.gif)
1 void Free(); //释放内存
2 void Free()
3 {
4 while (Phead->next != NULL)
5 {
6 Phead = Phead->next;
7 free(Phead);
8 }
9 free(Phead);
10 }
![复制代码](https://img-blog.csdnimg.cn/img_convert/88f9dfd897e98444eb9d6b5f032869de.gif)
附录:完整代码#
#include
#include
#include
#define HEIGHT 20 //设置地图高度
#define WIDTH 40 //设置地图宽度
#define PRINTF printf("■");
#define LINE printf("\n");
#define EMPTY printf(" ");
typedef struct Snakebody
{
int x, y;//身体的坐标
struct Snakebody *next;//结构指针
}Snakebody;//先来创建保持身体的链表,贪吃蛇的核心代码就是该如何保存蛇的身体
typedef struct Snakexy
{
int x;
int y;
}Snakexy; //记录食物坐标
int sum = 0; //计算得分
int JudgeSum = 0; //判断是否加快
int Hard = 0; //计算难度
int Pause = 200000000; //暂停速度(移动速度)
int JudgeDirection = 4; //判断方向
int * PJ = &JudgeDirection; //用指针传值判断移动方向
Snakebody *Phead = NULL; //存储着整个蛇身 不可更改
Snakebody *Phead_1 = NULL; //指向蛇身
Snakebody *Pbady = NULL; //创建节点
Snakebody *end = NULL; //尾节点
Snakexy * Food = NULL; //保存食物位置
void Front(); //游戏开始页面1
void Jfood(); //检测是否吃到食物1
void Jwall(); //检测蛇头是否撞墙1
void Jsnake(); //检测蛇头是否撞到蛇身1
void ISnake(); //初始化蛇身1
void DeawMap(); //绘制地图1
void FoodRand(); //生成食物1
void ControlMove(); //控制移动和暂停1
void MoveCursor(int x, int y); //移动光标1
void Move(); //游戏运行1
void Showf(); //显分数以及难度1
void Free(); //释放内存
int main()
{
Front();
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_GREEN);//绿
DeawMap();
Showf();
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_INTENSITY);// 暗白
MoveCursor(34, 10);
printf("↑");
MoveCursor(31, 11);
printf("使用←↓→来控制");
MoveCursor(31, 12);
printf("蛇的移动,撞墙游");
MoveCursor(31, 13);
printf("戏结束,每5分增 ");
MoveCursor(31, 14);
printf("一个难度(速度)");
ISnake();
FoodRand();
MoveCursor(40, 20);
Move();
return 0;
}
void Front()
{
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_INTENSITY | FOREGROUND_RED | FOREGROUND_BLUE);//设置红色和蓝色相加
MoveCursor(18, 15);
printf("请等待......");
for (int i = 0; i y == y)
{
x = rand() % 27 + 2;
y = rand() % 17 + 2;
}
else
{
Phead_1 = Phead_1->next;
}
if (Phead_1->next == NULL)
{
break;
}
}
MoveCursor(x, y);
PRINTF
Food = (Snakexy*)malloc(sizeof(Snakexy));
Food->x = x;
Food->y = y;
MoveCursor(33, 5);
printf(" ");
Showf();
sum++;
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_INTENSITY);// 蓝
}
void ControlMove()
{
if (GetAsyncKeyState(VK_UP) && 0x8000)
{
if (JudgeDirection == 2)
{
}
else
{
JudgeDirection = 1;
}
}
if (GetAsyncKeyState(VK_DOWN) && 0x8000)
{
if (JudgeDirection == 1)
{
}
else
{
JudgeDirection = 2;
}
}
if (GetAsyncKeyState(VK_RIGHT) && 0x8000)
{
if (JudgeDirection == 3)
{
}
else
{
JudgeDirection = 4;
}
}
if (GetAsyncKeyState(VK_LEFT) && 0x8000)
{
if (JudgeDirection == 4)
{
}
else
{
JudgeDirection = 3;
}
}
if (GetAsyncKeyState(VK_RETURN) && 0x0D)
{
while (1)
{
if (GetAsyncKeyState(VK_RETURN) && 0x0D)
{
break;
}
}
}
}
void ISnake()
{
for (int i = 0; i < 5; i++)
{
Pbady = (Snakebody*)malloc(sizeof(Snakebody));
Pbady->x = 5 - i;
Pbady->y = 5;
if (Phead == NULL)
{
Phead = Pbady;
}
else
{
end->next = Pbady;
}
Pbady->next = NULL;
end = Pbady;
}
Phead_1 = Phead;
while (Phead_1->next != NULL)
{
MoveCursor(Phead_1->x, Phead_1->y);
PRINTF
Phead_1 = Phead_1->next;
}
}
void Move()
{
while (1)
{
Phead_1 = Phead;
while (Phead_1->next->next != NULL)
{
Phead_1 = Phead_1->next;
}
Phead_1->next = NULL;
for (int i = 0; i < Pause; i++) {}
ControlMove();
MoveCursor(Phead_1->x, Phead_1->y);
EMPTY
//上面为消除尾部
Snakebody *Phead_2 = (Snakebody*)malloc(sizeof(Snakebody));
if (*PJ == 1)
{
Phead_2->x = Phead->x;
Phead_2->y = Phead->y - 1;
}
if (*PJ == 2)
{
Phead_2->x = Phead->x;
Phead_2->y = Phead->y + 1;
}
if (*PJ == 3)
{
Phead_2->x = Phead->x - 1;
Phead_2->y = Phead->y;
}
if (*PJ == 4)
{
Phead_2->x = Phead->x + 1;
Phead_2->y = Phead->y;
}
Phead_2->next = Phead;
Phead = Phead_2;
MoveCursor(Phead_2->x, Phead_2->y);
PRINTF
Jfood();
Jwall();
Jsnake();
MoveCursor(40, 20);
}
}
void Jfood()
{
Phead_1 = Phead;
if (Phead_1->x == Food->x&&Phead_1->y == Food->y)
{
FoodRand();
JudgeSum += 1;
if (JudgeSum == 5)
{
JudgeSum = 0;
Hard += 1;
Pause -= 20000000;
}
while (Phead_1->next != NULL)
{
Phead_1 = Phead_1->next;
}
Snakebody *S = (Snakebody*)malloc(sizeof(Snakebody));
S->x = Food->x;
S->y = Food->y;
S->next = NULL;
Phead_1->next = S;
ControlMove();
MoveCursor(Phead_1->x, Phead_1->y);
PRINTF
}
//获取食物的坐标和蛇头做对比
}
void Jwall()
{
if (Phead->x == 0 || Phead->x == 29 || Phead->y == 0 || Phead->y == 19)
{
MoveCursor(10, 20);
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_INTENSITY | FOREGROUND_RED);//设置红色
printf("抱歉,你撞到了自己,游戏结束! ");
system("pause>nul");
exit(0);
}
}
void Jsnake()
{
Phead_1 = Phead->next;
while (Phead_1->next != NULL)
{
if ((Phead->x == Phead_1->x) && (Phead->y == Phead_1->y))
{
MoveCursor(10, 20);
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_INTENSITY | FOREGROUND_RED);//设置红色
printf("抱歉,你撞到了自己,游戏结束! ");
system("pause>nul");
exit(0);
}
Phead_1 = Phead_1->next;
}
}
void Showf()
{
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_BLUE);// 蓝
MoveCursor(33, 5);
printf("得分:%d", sum);
MoveCursor(33, 6);
printf("难度:%d", Hard);
}
void Free()
{
while (Phead->next != NULL)
{
Phead = Phead->next;
free(Phead);
}
free(Phead);
}
|